package com.bootcamp.javase2.threadpool;

import java.io.*;
import java.util.concurrent.*;

/**
 * @program: day21CallableCode
 * @description:
 * @author: Keyu Li
 * @create: 2021-09-15 15:44
 **/

public class CallCopyFile {
    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(5);
        // 多个线程将同一文件独立的复制多次，因此每次创建新的 callable 实现对象。
        // 使用同一个 callable 对象应该也可以，因为文件输入输出流定义在方法中，不属于对象属性，不会产生数据安全问题。
        String src = "D:\\wd\\copythreads\\jiyu.exe";
        FileWriter writer = new FileWriter("copydestination.txt");
        // 将结果输出到 future，避免get方法阻塞，导致多线程退化为单线程
        Future<String>[] fs = new Future[10];
        for (int i = 0; i < 10; i++) {
            fs[i] = pool.submit(new CallCopy(i, src));
        }
        pool.shutdown();
        for (int i = 0; i < 10; i++) {
            writer.write(fs[i].get());
            writer.write(System.lineSeparator());
        }
        writer.close();
    }
}

class CallCopy implements Callable<String> {
    int copyId;
    String src;
    double srcSizeMB;

    // 设置源文件地址和复制文件id
    public CallCopy(int copyId, String src) {
        this.copyId = copyId;
        this.src = src;
        this.srcSizeMB = new File(src).length() * 1.0 / (1 << 20);
    }

    @Override
    public String call() throws Exception {

        FileInputStream in = new FileInputStream(src);
        //设置 复制文件路径名
        String fileDst = src.replace(".exe", "")
                + copyId + ".exe";
        FileOutputStream out = new FileOutputStream(fileDst);
        byte[] bytes = new byte[1 << 20];
        int count = 0;
        while (in.read(bytes) != -1) {
            out.write(bytes);
            count++;
            System.out.println(Thread.currentThread().getName()
                    + " copying, "
                    + String.format("%2.2f", count * 100 / srcSizeMB)
                    + "% completed!!!");
        }
        // close
        in.close();
        out.close();
        // 返回复制文件名
        return fileDst;
    }
}
